<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>关系图</title>
</head>

<body>
    <canvas id="canvas" width="600" height="500">您的浏览器不支持canvans</canvas>
</body>
<script>
    const canvans = document.getElementById('canvas')
    const ctx = canvans.getContext('2d')

    const getPixelRatio = context => {
        return window.devicePixelRatio || 1
    }

    /**
     *  01. 放大canvas
     *  02. 在css中将宽高设置为原来的大小
     *  03. 考虑到内容的缩放，因此也需要将ctx 缩放
     * */
    const ratio = getPixelRatio()
    const oldWith = document.documentElement.clientWidth - 10
    const oldHeight = document.documentElement.clientHeight - 20
    // 放大canvas
    canvans.width = document.documentElement.clientWidth * ratio
    canvans.height = document.documentElement.clientHeight * ratio
    // css中设置为原来的大小
    canvans.style.width = oldWith + 'px'
    canvans.style.height = oldHeight + 'px'
    // 缩放ctx
    ctx.scale(ratio, ratio)


    class Ball {
        constructor(options) {
            this.canvans = options.canvans
            this.text = options.title
            this.ctx = this.canvans.getContext('2d')
            this.wd = this.canvans.clientWidth * ratio
            this.ht = this.canvans.clientHeight * ratio
            this.r = Math.random() * 40 + 10
            this.x = Math.random() * (this.wd - (this.r * 2)) + this.r
            this.y = Math.random() * (this.ht - (this.r * 2)) + this.r
            this.color = '#' + parseInt(Math.random() * 0xFFFFFF).toString(16)
            this.xSpeed = Math.random() * 4 + 6 // [6,10)
            this.ySpeed = Math.random() * 6 + 4 // [4,10)
            this.init()
        }

        init() {
            this.run()
            this.draw()
        }
        // 绘制
        draw() {
            this.drawCircle()
            this.drawText(this.text, this.x, this.y + this.r + 10)
        }
        // 绘制小球
        drawCircle() {
            this.ctx.beginPath()
            this.ctx.fillStyle = this.color
            this.ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
            this.ctx.fill()
            this.ctx.closePath()
        }
        // 绘制文字
        drawText(text, x, y) {
            this.ctx.font = 'normal 20px 微软雅黑'
            this.ctx.textAlign = 'center'
            this.ctx.textBaseline = 'middle'
            this.ctx.fillText(text, x, y)
        }
        // 绘制线条
        drawLine(x1, y1, x2, y2, color) {
            this.ctx.beginPath()
            this.ctx.lineWidth = 1
            this.ctx.strokeStyle = color || '#666'
            this.ctx.moveTo(x1, y1)
            this.ctx.lineTo(x2, y2)
            this.ctx.stroke()
            this.ctx.closePath()
        }
        /**
         *  小球运动边界处理
         * */
        run() {
            if (this.x - this.r <= 0 || this.x + this.r >= this.wd) {
                this.xSpeed = -this.xSpeed
            }
            if (this.y - this.r <= 0 || this.y + this.r >= this.ht) {
                this.ySpeed = -this.ySpeed
            }
            this.x += this.xSpeed
            this.y += this.ySpeed
        }
    }

    let ballArr = []
    const titleArr = ['Vue', 'React', 'Node', 'Angular', 'Webpack', 'Vite', 'eCharts', 'Next', 'Git', 'uniApp']
    for (let i = 0; i < 8; i++) {
        let ball = new Ball({
            canvans,
            title: titleArr[i]
        })
        ballArr.push(ball)

        // 连线
        for (let j = 0; j < i; j++) {
            const preBall = ballArr[j]
            ball.drawLine(ball.x, ball.y, preBall.x, preBall.y)
        }
    }

    // 动画
    setInterval(() => {
        ctx.clearRect(0, 0, canvas.clientWidth * 1.5, canvas.clientHeight * 1.5)
        for (let i = 0; i < ballArr.length; i++) {
            let ball = ballArr[i]
            // 连线
            for (let j = 0; j < i; j++) {
                const preBall = ballArr[j]
                ball.drawLine(ball.x, ball.y, preBall.x, preBall.y)
            }
        }
        for (let i = 0; i < ballArr.length; i++) {
            let ball = ballArr[i]
            ball.init()
        }
    }, 30)
</script>

</html>